package com.boarsoft.flow.exchange;

import java.io.File;
import java.io.IOException;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.ResourceEntityResolver;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.FileSystemResource;

import com.boarsoft.common.Util;
import com.boarsoft.common.util.FileUtil;
import com.boarsoft.flow.core.SimpleFlow;
import com.boarsoft.flow.core.SimpleFlowEngine;
import com.boarsoft.flow.store.SimpleFlowStore;

public class SimpleFlowExchangerImpl implements FlowExchanger, BeanFactoryAware, ApplicationContextAware {
	private static final Logger log = LoggerFactory.getLogger(SimpleFlowExchangerImpl.class);

	/** */
	protected DefaultListableBeanFactory beanFactory;
	/** */
	protected ConfigurableApplicationContext applicationContext;
	/** */
	protected SimpleFlowStore flowStore;
	/** */
	protected SimpleFlowEngine flowEngine;

	@Override
	public SimpleFlow exchange(String id, String code, String content, Map<String, String> refs) {
		// 关闭流程引擎（暂停服务）清除流程和对象缓存
		flowEngine.close();
		try {
			// 将XML写入文件
			String path = flowStore.getXmlPath(id);
			log.info("Recevied flow xml {}/{}.xml", path, code);
			String fp = new StringBuilder().append(path).append("/").append(code).append(".xml").toString();
			try {
				File f = new File(fp);
				FileUtil.makePath(f.getParent());
				FileUtil.write(f, content);
			} catch (Exception e) {
				log.error("Error on write bean definition to xml {}", fp, e);
				return null;
			}
			// 将XML转换为BEAN
			log.info("Load beans from {}", fp);
			XmlBeanDefinitionReader xbdr = new XmlBeanDefinitionReader(beanFactory);
			xbdr.setResourceLoader(applicationContext);
			xbdr.setEntityResolver(new ResourceEntityResolver(applicationContext));
			try {
				xbdr.loadBeanDefinitions(new FileSystemResource(fp));
			} catch (Exception e) {
				log.error("Error on load bean definitions from {}", fp, e);
				return null;
			}
			if (applicationContext.containsBean(code)) {
				log.info("Load bean {} from {} sucessfully", code, fp);
				return (SimpleFlow) applicationContext.getBean(code);
			}
			log.error("Load beans from {} failed", fp);
		} finally {
			flowEngine.open();
		}
		return null;
	}

	/**
	 * 
	 * @param id
	 * @param ref
	 * @param refs
	 * @return
	 */
	public boolean checkRefFlow(String id, String ref, Map<String, String> refs) {
		log.debug("Check referenced flow {} of {}", ref, id);
		if (applicationContext.containsBean(ref)) {
			// TODO ? 是否考虑版本？
			// SimpleFlow f = (SimpleFlow) applicationContext.getBean(ref);
			// if (f.isHigherThan(ver))
			return true;
		}
		// 如果不存在就尝试到flows.dir读取文件
		String fp = flowStore.getXmlFilePath(id, ref);
		File file = new File(fp);
		if (file.exists()) {
			try {
				String fd = FileUtil.read(file, "UTF-8");
				if (Util.strIsEmpty(fd)) {
					log.warn("Flow xml file {} is empty", fp);
				} else {
					// 如果有就exchanger加载到内存并执行
					SimpleFlow fo = this.exchange(id, ref, fd, refs);
					return fo != null;
				}
			} catch (IOException e) {
				log.warn("Error on read flow xml file {}", fp, e);
				return false;
			}
		}
		log.error("Flow xml {} does not exists", fp);
		return false;
	}

	/**
	 * 导入／更新流程
	 * 
	 * @param id
	 * @param flowId
	 * @param flowPath
	 * @param refs
	 */
	@Override
	public boolean importFlow(String id, String flowId, String flowPath, Map<String, String> refs) {
		// 如果该流程已被加载，则移除
		if (applicationContext.containsBean(flowId)) {
			applicationContext.getBeanFactory().destroyBean(flowId);
		}
		File flowXml = new File(flowPath);
		if (flowXml.exists()) {
			try {
				String fd = FileUtil.read(flowXml, "UTF-8");
				if (Util.strIsEmpty(fd)) {
					log.warn("Flow xml file {} is empty", flowPath);
				} else {
					SimpleFlow fo = this.exchange(id, flowId, fd, refs);
					return fo != null;
				}
			} catch (IOException e) {
				log.warn("Error on read flow xml file {}", flowPath, e);
				return false;
			}
		}
		log.error("Flow xml {} does not exists", flowPath);
		return false;
	}

	@Override
	public void setApplicationContext(ApplicationContext ctx) throws BeansException {
		this.applicationContext = (ConfigurableApplicationContext) ctx;
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = (DefaultListableBeanFactory) beanFactory;
	}

	public SimpleFlowStore getFlowStore() {
		return flowStore;
	}

	public void setFlowStore(SimpleFlowStore flowStore) {
		this.flowStore = flowStore;
	}

	public SimpleFlowEngine getFlowEngine() {
		return flowEngine;
	}

	public void setFlowEngine(SimpleFlowEngine flowEngine) {
		this.flowEngine = flowEngine;
	}
}
